const std = @import("std");
// const targets: []const std.Target.Query = &.{
//     .{ .cpu_arch = .aarch64, .os_tag = .macos },
//     .{ .cpu_arch = .aarch64, .os_tag = .linux },
//     .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .gnu },
//     .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
//     .{ .cpu_arch = .x86_64, .os_tag = .windows },
// };
pub fn zlbmath_print() void {
    std.debug.print("Hello, World!  zlbmath\n", .{});
}
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
//
fn buildOne(
    b: *std.Build,
    target: std.Build.ResolvedTarget,
    optimize: std.builtin.OptimizeMode,
) void {
    const lib_mod = b.addModule("zlbmath", .{
        // `root_source_file` is the Zig "entry point" of the module. If a module
        // only contains e.g. external object files, you can make this `null`.
        // In this case the main source file is merely a path, however, in more
        // complicated build scripts, this could be a generated file.
        .root_source_file = b.path("src/root.zig"),
        .target = target,
        .optimize = optimize,
    });

    // exe_mod.addImport("funmath", math_mod);
    // Now, we will create a static library based on the module we created above.
    // This creates a `std.Build.Step.Compile`, which is the build step responsible
    // for actually invoking the compiler.
    const lib = b.addLibrary(.{
        .linkage = .static,
        .name = "zlbmath",
        .root_module = lib_mod,
    });

    // This declares intent for the library to be installed into the standard
    // location when the user invokes the "install" step (the default step when
    // running `zig build`).
    b.installArtifact(lib);
    if (target.result.os.tag == .emscripten) {
        return;
    }
    // We will also create a module for our other entry point, 'main.zig'.
    const exe_mod = b.createModule(.{
        // `root_source_file` is the Zig "entry point" of the module. If a module
        // only contains e.g. external object files, you can make this `null`.
        // In this case the main source file is merely a path, however, in more
        // complicated build scripts, this could be a generated file.
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Modules can depend on one another using the `std.Build.Module.addImport` function.
    // This is what allows Zig source code to use `@import("foo")` where 'foo' is not a
    // file path. In this case, we set up `exe_mod` to import `lib_mod`.
    exe_mod.addImport("zlbmath_lib", lib_mod);
    // This creates another `std.Build.Step.Compile`, but this one builds an executable
    // rather than a static library.
    const exe = b.addExecutable(.{
        .name = "zlbmath",
        .root_module = exe_mod,
    });

    // This declares intent for the executable to be installed into the
    // standard location when the user invokes the "install" step (the default
    // step when running `zig build`).
    b.installArtifact(exe);

    // This *creates* a Run step in the build graph, to be executed when another
    // step is evaluated that depends on it. The next line below will establish
    // such a dependency.
    const run_cmd = b.addRunArtifact(exe);

    // By making the run step depend on the install step, it will be run from the
    // installation directory rather than directly from within the cache directory.
    // This is not necessary, however, if the application depends on other installed
    // files, this ensures they will be present and in the expected location.
    run_cmd.step.dependOn(b.getInstallStep());

    // This allows the user to pass arguments to the application in the build
    // command itself, like this: `zig build run -- arg1 arg2 etc`
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    // This creates a build step. It will be visible in the `zig build --help` menu,
    // and can be selected like this: `zig build run`
    // This will evaluate the `run` step rather than the default, which is "install".
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    // Creates a step for unit testing. This only builds the test executable
    // but does not run it.
    // const lib_unit_tests = b.addTest(.{
    //     .root_module = lib_mod,
    // });

    // const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

    const exe_unit_tests = b.addTest(.{
        .root_module = exe_mod,
    });

    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);

    // Similar to creating the run step earlier, this exposes a `test` step to
    // the `zig build --help` menu, providing a way for the user to request
    // running the unit tests.
    const test_step = b.step("test", "Run unit tests");

    const abs_dir_src = b.build_root.join(b.allocator, &.{"src"}) catch |err| {
        std.debug.print("Error joining directory: {}\n", .{err});
        return;
    };
    defer b.allocator.free(abs_dir_src);
    var open_dir = std.fs.openDirAbsolute(abs_dir_src, .{ .iterate = true }) catch |err| {
        std.debug.print("open directory failed: {}\n", .{err});
        return;
    };
    defer open_dir.close();
    var walker = open_dir.walk(b.allocator) catch |err| {
        std.debug.print("walk directory failed: {}\n", .{err});
        return;
    };
    defer walker.deinit();
    while (walker.next() catch null) |entry| {
        if (!std.mem.endsWith(u8, entry.path, ".zig")) {
            continue;
        }
        const relative_path = std.fs.path.join(b.allocator, &.{ "src", entry.path }) catch |err| {
            std.debug.print("join path failed: {}\n", .{err});
            return;
        };
        // const exe_mod = b.createModule(.{
        //     // `root_source_file` is the Zig "entry point" of the module. If a module
        //     // only contains e.g. external object files, you can make this `null`.
        //     // In this case the main source file is merely a path, however, in more
        //     // complicated build scripts, this could be a generated file.
        //     .root_source_file = b.path("src/main.zig"),
        //     .target = target,
        //     .optimize = optimize,
        // });
        // std.debug.print("relative path:{s}\n", .{relative_path});
        const testone = b.addTest(.{
            .root_module = b.createModule(.{
                .root_source_file = b.path(relative_path),
                .target = target,
                .optimize = optimize,
            }),
            // .root_source_file = b.path(relative_path),
            // .optimize = optimize,
            // .target = target,
        });
        testone.root_module.addImport("zlbmath_lib", lib_mod);
        const run_test = b.addRunArtifact(testone);
        test_step.dependOn(&run_test.step);
    }
    // const object = b.addObject(.{
    //     .name = "object",
    //     // .target = target,
    //     // .optimize = optimize,
    //     // .root_source_file = b.path("src/root.zig"),
    // });
    // const docs_install = b.addInstallDirectory(.{
    //     .source_dir = object.getEmittedDocs(),
    //     .install_dir = .prefix,
    //     .install_subdir = "docs",
    // });
    // const docs_step = b.step("docs", "generate docs");
    // docs_step.dependOn(&docs_install.step);
    // test_step.dependOn(&run_lib_unit_tests.step);
    test_step.dependOn(&run_exe_unit_tests.step);
}
pub fn build(b: *std.Build) void {
    // Standard target options allows the person running `zig build` to choose
    // what target to build for. Here we do not override the defaults, which
    // means any target is allowed, and the default is native. Other options
    // for restricting supported target set are available.

    const target = b.standardTargetOptions(.{});

    // Standard optimization options allow the person running `zig build` to select
    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
    // set a preferred release mode, allowing the user to decide how to optimize.
    const optimize = b.standardOptimizeOption(.{});

    // const targets = [_]std.Build.ResolvedTarget{
    //     b.resolveTargetQuery(.{
    //         .cpu_arch = null,
    //         .os_tag = null,
    //     }),
    //     b.resolveTargetQuery(.{
    //         .cpu_arch = .wasm32,
    //         .os_tag = .emscripten,
    //     }),
    // };
    // for (targets) |t| {
    //     buildOne(b, t, optimize);
    //     // if (t.result.os.tag == .emscripten) {
    //     //     buildWeb(b, t);
    //     // } else {
    //     //     builddesktop(b, t);
    //     // }
    // }
    buildOne(b, target, optimize);

    // This creates a "module", which represents a collection of source files alongside
    // some compilation options, such as optimization mode and linked system libraries.
    // Every executable or library we compile will be based on one or more modules.
    //
    // const math_mod = b.createModule(.{
    //     .root_source_file = b.path("src/math"),
    //     .target = target,
    //     .optimize = optimize,
    // });

}
